Passed
Push — master ( 13e950...ecf0b5 )
by EMP
01:40
created

main.js ➔ displayMsg   F

Complexity

Conditions 33

Size

Total Lines 109
Code Lines 83

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 83
dl 0
loc 109
rs 0
c 0
b 0
f 0
cc 33

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like main.js ➔ displayMsg often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"use strict";
2
3
sodium.ready.then(function() {
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
4
5
const ae = new AllEars(function(ok) {
1 ignored issue
show
Bug introduced by
The variable AllEars seems to be never declared. If this is a global, consider adding a /** global: AllEars */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
6
	if (ok) {
7
		const greeting = localStorage.greeting;
1 ignored issue
show
Bug introduced by
The variable localStorage seems to be never declared. If this is a global, consider adding a /** global: localStorage */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
8
		if (greeting) {
9
			document.getElementById("greeting").textContent = greeting;
10
			document.getElementById("txt_pg").value = greeting;
11
		}
12
13
		document.getElementById("txt_skey").style.background = "#466";
14
		document.getElementById("txt_skey").maxLength = "64";
15
	} else {
16
		console.log("Failed to load All-Ears");
17
	}
18
});
19
20
function TabState(cur, max, btnDele, btnUpdt) {
21
	this.cur = cur;
22
	this.max = max;
23
	this.btnDele = btnDele;
24
	this.btnUpdt = btnUpdt;
25
}
26
27
const tabs = [
28
	new TabState(0, 0, false, true), // Inbox
29
	new TabState(0, 0, false, true), // Outbx
30
	new TabState(0, 2, true, false), // Write
31
	new TabState(0, 2, false, false), // Notes
32
	new TabState(0, 3, false, true) // Tools
33
];
34
35
let showHeaders = false;
36
37
let tab = 0;
38
const TAB_INBOX = 0;
39
const TAB_OUTBX = 1;
0 ignored issues
show
Unused Code introduced by
The constant TAB_OUTBX seems to be never used. Consider removing it.
Loading history...
40
const TAB_WRITE = 2;
41
const TAB_NOTES = 3;
42
const TAB_TOOLS = 4;
43
44
// Helper functions
45
function getCountryName(countryCode) {
46
	const opts = document.getElementById("gatekeeper_country");
47
48
	for (let i = 0; i < opts.length; i++) {
49
		if (opts[i].value === countryCode) {
50
			return opts[i].textContent;
51
		}
52
	}
53
54
	return "Unknown countrycode: " + countryCode;
55
}
56
57
function getCountryFlag(countryCode) {
58
	return sodium.to_string(new Uint8Array([
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
59
		240, 159, 135, 166 + countryCode.codePointAt(0) - 65,
60
		240, 159, 135, 166 + countryCode.codePointAt(1) - 65
61
	]));
62
}
63
64
function getMsgId(num) {
65
	let i;
66
	if (ae.GetExtMsgHeaders(num).toLowerCase().slice(0, 11) === "message-id:") {
67
		i = 0;
68
	} else {
69
		i = ae.GetExtMsgHeaders(num).toLowerCase().indexOf("\nmessage-id:");
70
		if (i < 1) return "ERR";
71
		i++;
72
	}
73
74
	const x = ae.GetExtMsgHeaders(num).slice(i + 11).trim();
75
	if (x[0] !== "<") return "ERR2";
76
	return x.slice(1, x.indexOf(">"));
77
}
78
79
function clearDisplay() {
80
	let el = document.getElementById("midright").getElementsByTagName("img");
81
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("audio");
82
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("video");
83
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("embed");
84
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("iframe");
85
	if (el.length !== 1) return;
86
87
	URL.revokeObjectURL(el[0].src);
1 ignored issue
show
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
88
	el[0].remove();
89
}
90
91
function displayFile(num) {
92
	clearDisplay();
93
94
	document.getElementById("midright").scroll(0, 0);
95
	document.getElementById("btn_reply").disabled = true;
96
	document.getElementById("btn_mdele").disabled = true;
97
98
	document.getElementById("midright").children[0].hidden = true;
99
	document.getElementById("midright").children[1].textContent = ae.GetUplMsgTitle(num);
100
101
	switch (ae.GetUplMsgType(num)) {
102
		case "text": {
103
			document.getElementById("midright").children[2].hidden = false;
104
			document.getElementById("midright").children[2].textContent = sodium.to_string(ae.GetUplMsgBody(num));
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
105
		break;}
106
107
		case "image": {
108
			document.getElementById("midright").children[2].hidden = true;
109
			const img = document.createElement("img");
110
			img.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
2 ignored issues
show
Bug introduced by
The variable Blob seems to be never declared. If this is a global, consider adding a /** global: Blob */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
111
			document.getElementById("midright").appendChild(img);
112
113
			img.onclick = function() {
114
				if (!document.fullscreen)
115
					img.requestFullscreen();
116
				else
117
					document.exitFullscreen();
118
			};
119
		break;}
120
121
		case "audio": {
122
			document.getElementById("midright").children[2].hidden = true;
123
			const el = document.createElement("audio");
124
			el.controls = "controls";
125
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
126
			document.getElementById("midright").appendChild(el);
127
		break;}
128
129
		case "video": {
130
			document.getElementById("midright").children[2].hidden = true;
131
			const el = document.createElement("video");
132
			el.controls = "controls";
133
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
134
			document.getElementById("midright").appendChild(el);
135
		break;}
136
137
		case "pdf": {
138
			document.getElementById("midright").children[2].hidden = true;
139
			const el = document.createElement("embed");
140
			el.type = "application/pdf";
141
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer], {type: "application/pdf"}));
142
			document.getElementById("midright").appendChild(el);
143
		break;}
144
145
		case "html": {
146
			document.getElementById("midright").children[2].hidden = true;
147
			const el = document.createElement("iframe");
148
			el.allow = "";
149
			el.sandbox = "";
150
			el.csp = "base-uri 'none'; child-src 'none'; connect-src 'none'; default-src 'none'; font-src 'none'; form-action 'none'; frame-ancestors 'none'; frame-src 'none'; img-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'none'; style-src 'none'; worker-src 'none';";
151
			el.srcdoc = sodium.to_string(ae.GetUplMsgBody(num).buffer);
152
			document.getElementById("midright").appendChild(el);
153
		break;}
154
	}
155
}
156
157
function displayMsg(isInt, num) {
158
	clearDisplay();
159
160
	document.getElementById("midright").scroll(0, 0);
161
162
	const ts = isInt? ae.GetIntMsgTime(num) : ae.GetExtMsgTime(num);
163
164
	document.getElementById("btn_reply").disabled = false;
165
	document.getElementById("btn_reply").onclick = function() {
166
		document.getElementById("write_recv").value = isInt? ae.GetIntMsgFrom(num) : ae.GetExtMsgFrom(num);
167
		document.getElementById("write_subj").value = "Re: " + (isInt ? ae.GetIntMsgTitle(num) : ae.GetExtMsgTitle(num));
168
		document.getElementById("write_rply").textContent = (isInt? "" : getMsgId(num));
169
		document.getElementById("btn_write").click();
170
		document.getElementById("div_write_1").hidden = false;
171
		document.getElementById("div_write_2").hidden = true;
172
		document.getElementById("write_body").focus();
173
		for (const opt of document.getElementById("write_from").options) {
174
			if (opt.value === (isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgTo(num))) {
175
				opt.selected = true;
176
			}
177
		}
178
	};
179
180
	document.getElementById("btn_mdele").disabled = false;
181
	document.getElementById("btn_mdele").onclick = function() {
182
		this.blur();
183
184
		ae.Message_Delete(isInt? ae.GetIntMsgIdHex(num) : ae.GetExtMsgIdHex(num), function(success) {
185
			if (!success) console.log("Failed delete");
186
		});
187
	};
188
189
	document.getElementById("midright").children[0].hidden = false;
190
	document.getElementById("midright").children[2].hidden = false;
191
192
	if (isInt) {
193
		document.getElementById("midright").children[1].textContent = ae.GetIntMsgTitle(num);
194
		document.getElementById("midright").children[2].textContent = ae.GetIntMsgBody(num);
195
	} else {
196
		document.getElementById("midright").children[2].innerHTML = "";
197
198
		const headers = document.createElement("p");
199
		headers.textContent = ae.GetExtMsgHeaders(num);
200
		headers.className = "mono";
201
		headers.hidden = !showHeaders;
202
		document.getElementById("midright").children[2].appendChild(headers);
203
204
		const body = document.createElement("p");
205
		body.textContent = ae.GetExtMsgBody(num);
206
		document.getElementById("midright").children[2].appendChild(body);
207
208
		document.getElementById("midright").children[1].textContent = ae.GetExtMsgTitle(num);
209
		document.getElementById("midright").children[1].onclick = function() {showHeaders = !showHeaders; headers.hidden = !showHeaders;};
210
		document.getElementById("midright").children[1].style.cursor = "pointer";
211
	}
212
213
	document.getElementById("readmsg_to").textContent = isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgTo(num);
214
215
	const tzOs = new Date().getTimezoneOffset();
216
	const tz = ((tzOs < 0) ? "+" : "-") + Math.floor(tzOs / -60).toString().padStart(2, "0") + (tzOs % 60 * -1).toString().padStart(2, "0");
217
	document.getElementById("readmsg_date").children[0].textContent = new Date((ts * 1000) + (tzOs * -60000)).toISOString().slice(0, 19).replace("T", " ") + " " + tz;
218
219
	if (!isInt) {
220
		document.getElementById("readmsg_ip").hidden = false;
221
		document.getElementById("readmsg_country").hidden = false;
222
		document.getElementById("readmsg_tls").hidden = false;
223
		document.getElementById("readmsg_greet").hidden = false;
224
		document.getElementById("readmsg_timing").hidden = false;
225
		document.getElementById("readmsg_envfrom").hidden = false;
226
227
		const cc = ae.GetExtMsgCountry(num);
228
229
		document.getElementById("readmsg_ip").children[0].textContent = ae.GetExtMsgIp(num);
230
		document.getElementById("readmsg_country").textContent = getCountryFlag(cc) + " " + getCountryName(cc);
231
		document.getElementById("readmsg_tls").children[0].textContent = ae.GetExtMsgTLS(num);
232
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetExtMsgGreet(num);
233
		document.getElementById("readmsg_envfrom").textContent = ae.GetExtMsgFrom(num);
234
235
		let flagText = "";
236
		if (!ae.GetExtMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
237
		if (!ae.GetExtMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
238
		if (!ae.GetExtMsgFlagPExt(num)) flagText += "<abbr title=\"The sender did not use the Extended (ESMTP) protocol\">SMTP</abbr> ";
239
		if (!ae.GetExtMsgFlagQuit(num)) flagText += "<abbr title=\"The sender did not issue the required QUIT command\">QUIT</abbr> ";
240
		if (ae.GetExtMsgFlagRare(num)) flagText += "<abbr title=\"The sender issued unusual command(s)\">RARE</abbr> ";
241
		if (ae.GetExtMsgFlagFail(num)) flagText += "<abbr title=\"The sender issued invalid command(s)\">FAIL</abbr> ";
242
		if (ae.GetExtMsgFlagPErr(num)) flagText += "<abbr title=\"The sender violated the protocol\">PROT</abbr> ";
243
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
244
	} else {
245
		document.getElementById("readmsg_ip").hidden = true;
246
		document.getElementById("readmsg_country").hidden = true;
247
		document.getElementById("readmsg_tls").hidden = true;
248
		document.getElementById("readmsg_greet").hidden = true;
249
		document.getElementById("readmsg_timing").hidden = true;
250
		document.getElementById("readmsg_envfrom").hidden = true;
251
252
		let symbol = "<span title=\"Invalid level\">&#x26a0;</span>";
253
		if (ae.GetIntMsgFrom(num) === "system") {if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"System\">&#x1f162;</span>";} // S (System)
254
		else if (ae.GetIntMsgLevel(num) === 0) symbol = "<span title=\"Level 0 User\">&#x1f10c;</span>"; // 0
255
		else if (ae.GetIntMsgLevel(num) === 1) symbol = "<span title=\"Level 1 User\">&#x278a;</span>"; // 1
256
		else if (ae.GetIntMsgLevel(num) === 2) symbol = "<span title=\"Level 2 User\">&#x278b;</span>"; // 2
257
		else if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"Administrator\">&#x1f150;</span>"; // A (Admin)
258
		document.getElementById("readmsg_from").innerHTML = symbol + " " + ae.GetIntMsgFrom(num);
259
260
		let flagText = "";
261
		if (!ae.GetIntMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
262
		if (!ae.GetIntMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
263
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
264
	}
265
}
266
267
// Interface
268
function addMsg(isInt, i) {
269
	const row = document.getElementById("tbl_inbox").insertRow(-1);
270
	const cellTime = row.insertCell(-1);
271
	const cellSubj = row.insertCell(-1);
272
	const cellSnd1 = row.insertCell(-1);
273
	const cellSnd2 = row.insertCell(-1);
274
275
	const ts = isInt? ae.GetIntMsgTime(i) : ae.GetExtMsgTime(i);
276
	cellTime.setAttribute("data-ts", ts);
277
	cellTime.textContent = new Date((ts * 1000) + (new Date().getTimezoneOffset() * -60000)).toISOString().slice(0, 10);
278
279
	cellSubj.textContent = isInt? ae.GetIntMsgTitle(i) : ae.GetExtMsgTitle(i);
280
281
	if (isInt) {
282
		cellSnd1.textContent = ae.GetIntMsgFrom(i);
283
		cellSnd1.className = (ae.GetIntMsgFrom(i).length === 16) ? "mono" : "";
284
	} else {
285
		const from1 = ae.GetExtMsgFrom(i);
286
		const from2 = from1.substring(from1.indexOf("@") + 1);
287
		const cc = ae.GetExtMsgCountry(i);
288
289
		cellSnd1.textContent = from1.substring(0, from1.indexOf("@"));
290
291
		const flag = document.createElement("abbr");
292
		flag.textContent = getCountryFlag(cc);
293
		flag.title = getCountryName(cc);
294
		cellSnd2.appendChild(flag);
295
296
		const fromText = document.createElement("span");
297
		fromText.textContent = " " + from2;
298
		cellSnd2.appendChild(fromText);
299
	}
300
301
	row.onclick = function() {
302
		displayMsg(isInt, i);
303
	};
304
}
305
306
function getRowsPerPage() {
307
	const tbl = document.getElementById("tbl_inbox");
308
	tbl.innerHTML = "";
309
	const row = tbl.insertRow(-1);
310
	const cell = row.insertCell(-1);
311
	cell.textContent = "0";
312
313
	const rowsPerPage = Math.floor(getComputedStyle(document.getElementById("div_inbox")).height.replace("px", "") / getComputedStyle(document.querySelector("#tbl_inbox > tbody > tr:first-child")).height.replace("px", "")) - 1; // -1 allows space for 'load more'
314
	tbl.innerHTML = "";
315
	return rowsPerPage;
316
}
317
318
function addMessages() {
319
	const rowsPerPage = getRowsPerPage();
320
	let skipMsgs = rowsPerPage * tabs[TAB_INBOX].cur;
321
322
	const maxExt = ae.GetExtMsgCount();
323
	const maxInt = ae.GetIntMsgCount();
324
325
	tabs[TAB_INBOX].max = Math.floor((maxExt + maxInt) / rowsPerPage);
326
327
	let numExt = 0;
328
	let numInt = 0;
329
	let numAdd = 0;
330
331
	while (numAdd < rowsPerPage) {
332
		const tsInt = (numInt < maxInt) ? ae.GetIntMsgTime(numInt) : -1;
333
		const tsExt = (numExt < maxExt) ? ae.GetExtMsgTime(numExt) : -1;
334
		if (tsInt === -1 && tsExt === -1) break;
335
336
		if (tsInt !== -1 && (tsExt === -1 || tsInt > tsExt)) {
337
			if (skipMsgs > 0) skipMsgs--; else {addMsg(true, numInt); numAdd++;}
338
			numInt++;
339
		} else if (tsExt !== -1) {
340
			if (skipMsgs > 0) skipMsgs--; else {addMsg(false, numExt); numAdd++;}
341
			numExt++;
342
		}
343
	}
344
345
	if (ae.GetReadyMsgBytes() < ae.GetTotalMsgBytes()) {
346
		const inbox = document.getElementById("tbl_inbox");
347
		const row = inbox.insertRow(-1);
348
		const cell = row.insertCell(-1);
349
		cell.textContent = "Load more (" + (ae.GetTotalMsgBytes() - ae.GetReadyMsgBytes()) / 1024 + " KiB left)";
350
351
		row.onclick = function() {
352
			this.onclick = "";
353
354
			ae.Message_Browse(false, false, function(successBrowse) {
355
				document.getElementById("tbl_inbox").style.opacity = 1;
356
357
				if (successBrowse) {
358
					addMessages();
359
					addUploads();
360
					if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
361
				}
362
			});
363
		};
364
	}
365
}
366
367
function addUploads() {
368
	const tbl = document.getElementById("tbd_uploads");
369
	tbl.innerHTML = "";
370
371
	for (let i = 0; i < ae.GetUplMsgCount(); i++) {
372
		const row = tbl.insertRow(-1);
373
		let cell;
374
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetUplMsgTime(i) * 1000).toISOString().slice(0, 10);
375
376
		cell = row.insertCell(-1); cell.textContent = ae.GetUplMsgTitle(i);
377
		cell.onclick = function() {displayFile(this.parentElement.rowIndex - 1);};
378
379
		cell = row.insertCell(-1); cell.textContent = (ae.GetUplMsgBytes(i) / 1024).toFixed(1);
380
381
		cell = row.insertCell(-1);
382
		if (ae.GetUplMsgIdHex(i)) {
383
			cell.innerHTML = "<button data-msgid=\"" + ae.GetUplMsgIdHex(i) + "\" type=\"button\">X</button>";
384
385
			cell.children[0].onclick = function() {
386
				const tr = this.parentElement.parentElement;
387
				ae.Message_Delete(this.getAttribute("data-msgid"), function(success) {
388
					if (success) tr.remove();
389
				});
390
			};
391
		}
392
	}
393
}
394
395
function updateAddressCounts() {
396
	document.getElementById("limit_normal").textContent = (ae.GetAddressCountNormal() + "/" + ae.GetLimitNormalA(ae.GetUserLevel())).padStart(ae.GetLimitNormalA(ae.GetUserLevel()) > 9 ? 5 : 1);
397
	document.getElementById("limit_shield").textContent = (ae.GetAddressCountShield() + "/" + ae.GetLimitShieldA(ae.GetUserLevel())).padStart(ae.GetLimitShieldA(ae.GetUserLevel()) > 9 ? 5 : 1);
398
	document.getElementById("limit_total").textContent = ((ae.GetAddressCountNormal() + ae.GetAddressCountShield()) + "/" + ae.GetAddrPerUser()).padStart(5);
399
}
400
401
function adjustLevel(pubkey, level, c) {
402
	const fs = document.getElementById("fs_accs");
403
	fs.disabled = true;
404
405
	ae.Account_Update(pubkey, level, function(success) {
406
		fs.disabled = false;
407
408
		if (success) {
409
			c[4].textContent = level;
410
			c[5].children[0].disabled = (level === 3);
411
			c[6].children[0].disabled = (level === 0);
412
		}
413
	});
414
}
415
416
function addAccountToTable(i) {
417
	const tblAccs = document.getElementById("tbd_accs");
418
	const row = tblAccs.insertRow(-1);
419
	let cell;
420
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserPkHex(i);
421
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSpace(i);
422
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserNAddr(i);
423
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSAddr(i);
424
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserLevel(i);
425
426
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">+</button>";
427
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent) + 1, c);};
428
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 3);
429
430
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">&minus;</button>";
431
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent) - 1, c);};
432
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 0);
433
434
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">X</button>";
435
	cell.children[0].onclick = function() {
436
		const tr = this.parentElement.parentElement;
437
		ae.Account_Delete(tr.cells[0].textContent, function(success) {
438
			if (success) tr.remove();
439
		});
440
	};
441
}
442
443
function reloadAccount() {
444
	// Limits
445
	const tblLimits = document.getElementById("tbl_limits");
446
	if (ae.IsUserAdmin()) {
447
		for (let i = 0; i < 4; i++) {
448
			tblLimits.rows[i].cells[1].children[0].disabled = false;
449
			tblLimits.rows[i].cells[2].children[0].disabled = false;
450
			tblLimits.rows[i].cells[3].children[0].disabled = false;
451
452
			tblLimits.rows[i].cells[1].children[0].value = ae.GetLimitStorage(i) + 1;
453
			tblLimits.rows[i].cells[2].children[0].value = ae.GetLimitNormalA(i);
454
			tblLimits.rows[i].cells[3].children[0].value = ae.GetLimitShieldA(i);
455
		}
456
	} else {
457
		const lvl = ae.GetUserLevel();
458
		tblLimits.rows[lvl].cells[1].children[0].value = ae.GetLimitStorage(lvl) + 1;
459
		tblLimits.rows[lvl].cells[2].children[0].value = ae.GetLimitNormalA(lvl);
460
		tblLimits.rows[lvl].cells[3].children[0].value = ae.GetLimitShieldA(lvl);
461
	}
462
463
	// Accounts
464
	const tblAccs = document.getElementById("tbd_accs");
465
466
	// All: Our account
467
	const row = tblAccs.insertRow(-1);
468
	let cell;
469
	cell = row.insertCell(-1); cell.textContent = ae.GetUserPkHex();
470
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
471
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountNormal();
472
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountShield();
473
	cell = row.insertCell(-1); cell.textContent = ae.GetUserLevel();
474
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\" disabled=\"disabled\">+</button>";
475
476
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_downme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">&minus;</button>";
477
	cell.children[0].onclick = function() {
478
		const newLevel = parseInt(row.cells[4].textContent) - 1;
479
		ae.Account_Update(ae.GetUserPkHex(), newLevel, function(success) {
480
			if (success) row.cells[4].textContent = newLevel;
481
		});
482
	};
483
484
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_killme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">X</button>";
485
	cell.children[0].onclick = function() {
486
		ae.Account_Delete(ae.GetUserPkHex(), function(success) {
487
			if (success) row.remove();
488
		});
489
	};
490
491
	document.getElementById("txt_reg").disabled = !ae.IsUserAdmin();
492
	document.getElementById("btn_reg").disabled = !ae.IsUserAdmin();
493
494
	// Contacts
495
	for (let i = 0; i < ae.GetContactCount(); i++) {
496
		addContact(
497
			ae.GetContactMail(i),
498
			ae.GetContactName(i),
499
			ae.GetContactNote(i)
500
		);
501
	}
502
503
	// Addresses
504
	for (let i = 0; i < ae.GetAddressCount(); i++) {
505
		addAddress(i);
506
	}
507
508
	updateAddressCounts();
509
	addMessages();
510
	addUploads();
511
}
512
513
function deleteAddress(addr) {
514
	let btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
515
	for (let i = 0; i < btns.length; i++) btns[i].disabled = true;
516
517
	let addressToDelete = -1;
518
519
	for (let i = 0; i < ae.GetAddressCount(); i++) {
520
		if (addr === ae.GetAddress(i)) {
521
			addressToDelete = i;
522
			break;
523
		}
524
	}
525
526
	if (addressToDelete === -1) return;
527
528
	ae.Address_Delete(addressToDelete, function(success) {
529
		if (success) {
530
			document.getElementById("tbl_addrs").deleteRow(addressToDelete);
531
			document.getElementById("write_from").remove(addressToDelete);
532
			updateAddressCounts();
533
534
			if (ae.GetAddressCountNormal() < ae.GetLimitNormalA(ae.GetUserLevel())) document.getElementById("btn_address_create_normal").disabled = false;
535
			if (ae.GetAddressCountShield() < ae.GetLimitShieldA(ae.GetUserLevel())) document.getElementById("btn_address_create_shield").disabled = false;
536
537
			ae.Private_Update(function(success2) {
538
				if (!success2) console.log("Failed to update the Private field");
539
540
				btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
541
				for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
542
			});
543
		} else {
544
			console.log("Failed to delete address");
545
546
			btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
547
			for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
548
		}
549
	});
550
}
551
552 View Code Duplication
function shieldMix(addr) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
553
	let newAddr = "";
554
555
	for (let i = 0; i < 16; i++) {
556
		switch (addr.charAt(i)) {
557
			case '1':
558
				newAddr += "1iIlL".charAt(Math.floor(Math.random() * 5));
559
				break;
560
			case '0':
561
				newAddr += "0oO".charAt(Math.floor(Math.random() * 3));
562
				break;
563
			case 'w':
564
				newAddr += "VvWw".charAt(Math.floor(Math.random() * 4));
565
				break;
566
			default:
567
				newAddr += (Math.random() > 0.5) ? addr.charAt(i) : addr.charAt(i).toUpperCase();
568
		}
569
	}
570
571
	return newAddr;
572
}
573
574
function addAddress(num) {
575
	const addrTable = document.getElementById("tbl_addrs");
576
	const row = addrTable.insertRow(-1);
577
	const cellAddr = row.insertCell(-1);
578
	const cellChk1 = row.insertCell(-1);
579
	const cellChk2 = row.insertCell(-1);
580
	const cellChk3 = row.insertCell(-1);
581
	const cellBtnD = row.insertCell(-1);
582
583
	cellAddr.textContent = ae.GetAddress(num);
584
	cellAddr.onclick = function() {
585
		if (cellAddr.textContent.length === 16)
586
			navigator.clipboard.writeText(shieldMix(cellAddr.textContent) + "@" + ae.GetDomainEml());
1 ignored issue
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
587
		else
588
			navigator.clipboard.writeText(cellAddr.textContent + "@" + ae.GetDomainEml());
589
	};
590
591
	cellChk1.innerHTML = ae.GetAddressAccExt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
592
	cellChk2.innerHTML = ae.GetAddressAccInt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
593
	cellChk3.innerHTML = ae.GetAddressUse_Gk(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
594
595
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
596
	cellBtnD.onclick = function() {deleteAddress(cellAddr.textContent);};
597
598
	const opt = document.createElement("option");
599
	opt.value = cellAddr.textContent;
600
	opt.textContent = cellAddr.textContent + "@" + ae.GetDomainEml();
601
	document.getElementById("write_from").appendChild(opt);
602
}
603
604
document.getElementById("btn_dele").onclick = function() {
605
	this.blur();
606
607
	if (tab === TAB_WRITE) {
608
		tabs[tab].cur = 0;
609
		updateTab();
610
611
		document.getElementById("write_recv").value = "";
612
		document.getElementById("write_subj").value = "";
613
		document.getElementById("write_body").value = "";
614
615
		document.getElementById("write_recv").focus();
616
	}
617
};
618
619
document.getElementById("btn_updt").onclick = function() {
620
	const btn = this;
621
	btn.disabled = true;
622
	btn.blur();
623
624
	if (tab === TAB_INBOX) {
625
		document.getElementById("tbl_inbox").style.opacity = 0.5;
626
627
		ae.Message_Browse(true, false, function(successBrowse) {
628
			document.getElementById("tbl_inbox").style.opacity = 1;
629
630
			if (successBrowse) {
631
				addMessages();
632
				addUploads();
633
				btn.disabled = false;
634
			} else {
635
				console.log("Failed to refresh");
636
				btn.disabled = false;
637
			}
638
		});
639
	}
640
};
641
642
function addContact(mail, name, note) {
643
	const tbl = document.getElementById("tbl_ctact");
644
	const row = tbl.insertRow(-1);
645
	const cellMail = row.insertCell(-1);
646
	const cellName = row.insertCell(-1);
647
	const cellNote = row.insertCell(-1);
648
	const cellBtnD = row.insertCell(-1);
649
650
	cellMail.textContent = mail;
651
	cellName.textContent = name;
652
	cellNote.textContent = note;
653
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
654
655
	cellMail.contentEditable = true;
656
	cellName.contentEditable = true;
657
	cellNote.contentEditable = true;
658
659
	cellBtnD.onclick = function() {row.remove();};
660
}
661
662
document.getElementById("btn_newcontact").onclick = function() {
663
	addContact("", "", "");
664
};
665
666
document.getElementById("btn_savecontacts").onclick = function() {
667
	while (ae.GetContactCount() > 0) {
668
		ae.DeleteContact(0);
669
	}
670
671
	for (const row of document.getElementById("tbl_ctact").rows) {
672
		ae.AddContact(row.cells[0].textContent, row.cells[1].textContent, row.cells[2].textContent);
673
	}
674
675
	const btn = this;
676
	btn.disabled = true;
677
678
	ae.Private_Update(function(success) {
679
		btn.disabled = false;
680
681
		if (!success) {
682
			console.log("Failed contacts update");
683
		}
684
	});
685
};
686
687
function updateTab() {
688
	switch (tab) {
689
		case TAB_INBOX:
690
			addMessages();
691
		break;
692
693
		case TAB_WRITE:
694
			switch (tabs[tab].cur) {
695
				case 0: // Write
696
					document.getElementById("div_write_1").hidden = false;
697
					document.getElementById("div_write_2").hidden = true;
698
					document.getElementById("write_body").focus();
699
				break;
700
701
				case 1: // Verify
702
					ae.Address_Lookup(document.getElementById("write_recv").value, function(pk) {
703
						if (pk) {
704
							document.getElementById("div_write_1").hidden = true;
705
							document.getElementById("div_write_2").hidden = false;
706
707
							document.getElementById("write2_from").textContent = document.getElementById("write_from").value + "@" + ae.GetDomainEml();
708
							document.getElementById("write2_recv").textContent = document.getElementById("write_recv").value;
709
							document.getElementById("write2_pkey").textContent = sodium.to_hex(pk);
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
710
711
							document.getElementById("write2_subj").textContent = document.getElementById("write_subj").value;
712
							document.getElementById("write2_rply").textContent = document.getElementById("write_rply").textContent;
713
							document.getElementById("write2_body").textContent = document.getElementById("write_body").value;
714
						} else {
715
							console.log("Failed lookup");
716
						}
717
					});
718
				break;
719
720
				case 2: // Send
721
					ae.Message_Create(
722
						document.getElementById("write_subj").value,
723
						document.getElementById("write_body").value,
724
						document.getElementById("write_from").value,
725
						document.getElementById("write_recv").value,
726
						document.getElementById("write_rply").textContent,
727
						(document.getElementById("write2_recv").textContent.indexOf("@") > 0) ? null : sodium.from_hex(document.getElementById("write2_pkey").textContent),
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
728
						function(success) {
729
							if (success) {
730
								console.log("Sent ok");
731
							} else {
732
								console.log("Failed sending");
733
							}
734
						}
735
					);
736
				break;
737
			}
738
		break;
739
740
		case TAB_NOTES:
741
			for (let i = 0; i <= tabs[tab].max; i++) {
742
				document.getElementById("div_notes").children[i].hidden = (i !== tabs[tab].cur);
743
			}
744
		break;
745
746
		case TAB_TOOLS:
747
			for (let i = 0; i <= tabs[tab].max; i++) {
748
				document.getElementById("div_tools").children[i].hidden = (i !== tabs[tab].cur);
749
			}
750
		break;
751
	}
752
753
	document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
754
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
755
}
756
757
document.getElementById("btn_left").onclick = function() {
758
	tabs[tab].cur--;
759
	if (tabs[tab].cur === 0) this.disabled = true;
760
	if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
761
	updateTab();
762
	this.blur();
763
};
764
765
document.getElementById("btn_rght").onclick = function() {
766
	tabs[tab].cur++;
767
	if (tabs[tab].cur === tabs[tab].max) this.disabled = true;
768
	document.getElementById("btn_left").disabled = false;
769
	updateTab();
770
	this.blur();
771
};
772
773
const buttons = document.querySelector("#main1 > .top").getElementsByTagName("button");
774
for (let i = 0; i < buttons.length; i++) {
775
	buttons[i].onclick = function() {
776
		tab = i;
777
778
		for (let j = 0; j < buttons.length; j++) {
779
			document.querySelector("#main1 > .mid").children[j].hidden = (tab !== j);
780
			buttons[j].disabled = (tab === j);
781
		}
782
783
		document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
0 ignored issues
show
Bug introduced by
The variable tab is changed as part of the for loop for example by i on line 776. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
784
		document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
785
		document.getElementById("btn_dele").disabled = !tabs[tab].btnDele;
786
		document.getElementById("btn_updt").disabled = !tabs[tab].btnUpdt;
787
	};
788
}
789
790
function addressCreate(addr) {
791
	const btnN = document.getElementById("btn_address_create_normal");
792
	const btnS = document.getElementById("btn_address_create_shield");
793
	btnN.disabled = true;
794
	btnS.disabled = true;
795
796
	ae.Address_Create(addr, function(success1) {
797
		if (success1) {
798
			ae.Private_Update(function(success2) {
799
				addAddress(ae.GetAddressCount() - 1);
800
				if (addr !== "SHIELD") document.getElementById("txt_address_create_normal").value = "";
801
				updateAddressCounts();
802
803
				if (!success2) console.log("Failed to update the Private field");
804
805
				if (ae.GetAddressCountNormal() < ae.GetLimitNormalA(ae.GetUserLevel())) btnN.disabled = false;
806
				if (ae.GetAddressCountShield() < ae.GetLimitShieldA(ae.GetUserLevel())) btnS.disabled = false;
807
			});
808
		} else {
809
			console.log("Failed to add address");
810
811
			if (ae.GetAddressCountNormal() < ae.GetLimitNormalA(ae.GetUserLevel())) btnN.disabled = false;
812
			if (ae.GetAddressCountShield() < ae.GetLimitShieldA(ae.GetUserLevel())) btnS.disabled = false;
813
		}
814
	});
815
}
816
817
document.getElementById("btn_address_create_normal").onclick = function() {
818
	if (ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel())) return;
819
820
	const txtNewAddr = document.getElementById("txt_address_create_normal");
821
	if (!txtNewAddr.reportValidity()) return;
822
823
	addressCreate(txtNewAddr.value);
824
};
825
826
document.getElementById("btn_address_create_shield").onclick = function() {
827
	if (ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel())) return;
828
829
	addressCreate("SHIELD");
830
};
831
832
document.getElementById("btn_reg").onclick = function() {
833
	const btn = document.getElementById("btn_reg");
834
	const txt = document.getElementById("txt_reg");
835
	if (!txt.reportValidity()) return;
836
	btn.disabled = true;
837
838
	ae.Account_Create(txt.value, function(success) {
839
		if (success) {
840
			addAccountToTable(ae.Admin_GetUserCount() - 1);
841
			txt.value = "";
842
		}
843
844
		btn.disabled = false;
845
	});
846
};
847
848
document.getElementById("chk_downme").onclick = function() {document.getElementById("btn_downme").disabled = !this.checked;};
849
document.getElementById("chk_killme").onclick = function() {document.getElementById("btn_killme").disabled = !this.checked;};
850
851
document.getElementById("btn_notepad_saveupl").onclick = function() {
852
	const np = document.getElementById("txt_notepad");
853
	np.disabled = true;
854
855
	let fname = prompt("Save as...", "Untitled");
0 ignored issues
show
Debugging Code Best Practice introduced by
The prompt UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
856
	if (!fname.endsWith(".txt")) fname += ".txt";
857
858
	ae.Message_Upload(fname, np.value, function(success) {
859
		if (success) {
860
			np.value = "";
861
			addUploads();
862
			document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
863
		}
864
865
		console.log("Failed to add text");
866
		np.disabled = false;
867
	});
868
};
869
870
document.getElementById("btn_upload").onclick = function() {
871
	const btn = this;
872
	const fileSelector = document.createElement("input");
873
	fileSelector.type = "file";
874
	fileSelector.click();
875
876
	fileSelector.onchange = function() {
877
		btn.disabled = true;
878
879
		const reader = new FileReader();
1 ignored issue
show
Bug introduced by
The variable FileReader seems to be never declared. If this is a global, consider adding a /** global: FileReader */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
880
		reader.onload = function() {
881
			ae.Message_Upload(fileSelector.files[0].name, new Uint8Array(reader.result), function(success) {
882
				if (success) {
883
					addUploads();
884
					document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
885
				} else {
886
					console.log("Failed upload");
887
				}
888
889
				btn.disabled = false;
890
			});
891
		};
892
893
		reader.readAsArrayBuffer(fileSelector.files[0]);
894
	};
895
};
896
897
document.getElementById("btn_pg").onclick = function() {
898
	localStorage.greeting = document.getElementById("txt_pg").value;
1 ignored issue
show
Bug introduced by
The variable localStorage seems to be never declared. If this is a global, consider adding a /** global: localStorage */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
899
};
900
901
document.getElementById("txt_skey").onkeyup = function(event) {
902
	if (event.key === "Enter") {
903
		event.preventDefault();
904
		document.getElementById("btn_enter").click();
905
	}
906
};
907
908
document.getElementById("btn_enter").onclick = function() {
909
	const txtSkey = document.getElementById("txt_skey");
910
	if (!txtSkey.reportValidity()) return;
911
912
	const btn = this;
913
	btn.disabled = true;
914
	document.getElementById("txt_skey").style.background = "#233";
915
916
	ae.SetKeys(txtSkey.value, function(successSetKeys) {
917
		if (successSetKeys) {
918
			ae.Message_Browse(false, true, function(successBrowse) {
919
				if (successBrowse) {
920
					txtSkey.value = "";
921
					document.getElementById("div_begin").hidden = true;
922
					document.getElementById("div_main").style.display = "grid";
923
					reloadAccount();
924
925
					if (ae.IsUserAdmin()) {
926
						ae.Account_Browse(function(successAcc) {
927
							if (successAcc) {for (let i = 0; i < ae.Admin_GetUserCount(); i++) {addAccountToTable(i);}}
928
							else console.log("Failed to Account_Browse");
929
						});
930
					}
931
				} else {
932
					console.log("Failed to enter");
933
					btn.disabled = false;
934
					document.getElementById("txt_skey").style.background = "#466";
935
					txtSkey.focus();
936
				}
937
			});
938
		} else {
939
			console.log("Invalid format for key");
940
			btn.disabled = false;
941
			document.getElementById("txt_skey").style.background = "#466";
942
			txtSkey.focus();
943
		}
944
	});
945
};
946
947
});
948